package com.yonyou.pmclouds.basecom.conf;

import com.alibaba.dubbo.config.annotation.Reference;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.device.DeviceInfoMaintain;
import com.yonyou.pmclouds.device.entity.DeviceInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.annotation.Order;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

@Configuration
public class DeviceStatisticConfiguration implements ApplicationListener {
    /**
     * 用于传递设备信息到存储线程，不阻塞、不影响业务线程，同时起到一定的缓冲作用<br>
     * 积压过多的设备信息时候，说明数据库处理能力有限，作为非核心业务，抛弃一些信息是没问题的
     */
    private BlockingQueue<DeviceInfoVO> bufferedInfos = new ArrayBlockingQueue<>(2 << 12);
    private DeviceInfoProcessThread thread = null;
    private ServiceWrapper serviceWrapper = new ServiceWrapper();

    @Bean
    @Order(FilterLevelConst.LEVEL_FORTH)
    public Filter deviceStatisticFilter() {
        return new DeviceStatisticFilter(bufferedInfos);
    }

    @Bean
    public ServiceWrapper deviceInfoServiceWrapper() {
        return this.serviceWrapper;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationReadyEvent) {
            // 启动监听线程
            thread = new DeviceInfoProcessThread(bufferedInfos, serviceWrapper.maintain);
            thread.start();
        } else if (event instanceof ContextClosedEvent) {
            // 结束监听线程
            thread.notifyStop();
        }
    }

    /**
     * 为了获取dubbo的service的一个包装器，直接configuration里面注解，不一定能获取到
     */
    public static class ServiceWrapper {

        @Reference(version = ApplicationConsts.APPLICATION_VERSION)
        private DeviceInfoMaintain maintain;
    }

    public static class DeviceStatisticFilter implements Filter {
        private BlockingQueue<DeviceInfoVO> buffer;

        public DeviceStatisticFilter(BlockingQueue<DeviceInfoVO> buffer) {
            this.buffer = buffer;
        }

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {

        }

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            try {
                HttpServletRequest request = (HttpServletRequest) servletRequest;
                String deviceId = request.getHeader("device-id");
                if (!StringUtils.isEmpty(deviceId)) {
                    // 提取数据，转换为vo
                    DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
                    deviceInfoVO.setPkInfo(UUID.randomUUID().toString());
                    deviceInfoVO.setDeviceId(deviceId);
                    deviceInfoVO.setDeviceTypeName(request.getHeader("device-type-name"));
                    deviceInfoVO.setOsName(request.getHeader("os"));
                    deviceInfoVO.setOsVersion(request.getHeader("os-version"));
                    deviceInfoVO.setUserId(RuntimeEnvironment.getLoginUserId());
                    // 将数据塞入queue中，处理queue的异常情况
                    buffer.offer(deviceInfoVO);
                }
            } finally {
                // 无论发生什么，继续请求
                filterChain.doFilter(servletRequest, servletResponse);
            }
        }

        @Override
        public void destroy() {

        }


    }

    /**
     * 处理设配信息的Thread
     */
    @Slf4j
    public static class DeviceInfoProcessThread extends Thread {
        private volatile boolean running = true;
        private BlockingQueue<DeviceInfoVO> buffer;
        private DeviceInfoMaintain maintain;

        public DeviceInfoProcessThread(BlockingQueue<DeviceInfoVO> buffer, DeviceInfoMaintain maintain) {
            this.buffer = buffer;
            this.maintain = maintain;
            setDaemon(true); //设置为后台线程
        }

        public void notifyStop() {
            running = false;
            this.interrupt();
        }

        @Override
        public void run() {
            while (running) {
                try {
                    // 从队列拉取数据
                    DeviceInfoVO take = buffer.take();
                    List<DeviceInfoVO> list = new ArrayList<>();
                    Set<String> infoSet = new HashSet<>();
                    list.add(take);
                    int count = 1;
                    // 等一会，看一下有没有其他新的设备进来，最差的情况，100s的丢失100条访问记录
                    while (count < 100 && (take = buffer.poll(1000, TimeUnit.MILLISECONDS)) != null) {
                        // 进行第一次去重
                        if (infoSet.add(take.getDeviceId() + take.getUserId())) {
                            list.add(take);
                        }
                        count++;
                    }
                    // 批量保存
                    maintain.insertDeviceInfos(list);
                    // 吃掉所有异常
                } catch (Exception e) {
                    log.error("设备存储信息异常", e);
                }
            }
        }
    }
}
